var ssid=0;

function SigSlot()
{
    var parent = null;
    var children = {};
    var id = ++ssid;
    var slotIndex = {};
    var childSlotIndex = {};
    
    SigSlot.prototype.addSlot = function( n, obj )
    {
        if( !(n in slotIndex) )
        {
            slotIndex[n] = {};
        }
        obj = obj || this;
        slotIndex[n][id] = obj;
        if( parent ) parent.addSlot( n, obj );
    };
    
    SigSlot.prototype.__setParent = function( p ){ 
        parent = p;
    };
    
    this.getParent = function(){ return parent; };
    
    this.signalUp = function( name )
    {
        if( parent )
        {
            parent.message.apply( parent, arguments );
            parent.signalDown.apply( parent, arguments );
        }
    };
    
    this.signalDown = function( name )
    {
        var si = slotIndex[name];
        if( !si ) return;
        var args = Array.prototype.slice.call( arguments, 1 );
        for( var k in si )
        {
            si[k][name].apply( si[k], args );
        }
    };
    
    this.shortSignal = function( name ){
        if( parent )
        {
            parent.message.apply( parent, arguments );
            parent.signalDown.apply( parent, arguments );
        }
        else
        {
            this.message.apply( this, arguments );
        }
        this.signalDown.apply(this, arguments);
    };
    
    this.message = function( name )
    {
        var args = Array.prototype.slice.call( arguments, 1 );
        if( typeof(this[name]) == "function" ) this[name].apply( this, args );
    };
    
    this.getId = function(){return id;};
    
    this.addChild = function( child )
    {
        var cid;
        if( !child || (cid=child.getId()) in children ) return;
        children[cid] = child;
        index( child.constructor.name, child );
        child.__setParent(this);
    };
    
    this.removeChild = function( child )
    {
        var cid;
        if( !child || !((cid=child.getId()) in children) ) return;
        delete children[ cid ];
        unindex( child.constructor.name, child );
    };
    
    function index( name, ss, id )
    {
        if( !(name in oboe.loadedClasses) ) return;
        
        id = id || ss.getId();
        
        var data = oboe.loadedClasses[ name ];
        
        if( data.base != "" ) index( data.base, ss, id );
        
        for( var m in data.tags )
        {
            if( !(m in slotIndex) )
            {
                slotIndex[ m ] = {};
            }
            slotIndex[ m ][ id ] = ss;
        }
    }
    
    function unindex( name, ss, id )
    {
        if( !(name in oboe.loadedClasses) ) return;
        id = id || ss.getId();
        var data = oboe.loadedClasses[ name ];
        if( data.base != "" ) unindex( data.base, ss, id );
        
        for( var m in data.tags )
        {
            var si = slotIndex[ m ];
            if( si && id in si )
            {
                delete si[ id ];
                for( var t in si )
                {
                    si = null;
                    break;
                }
                if( si )
                {
                    delete slotIndex[ m ];
                }
            }
        }
    }
    
    // index.apply( this, [this.constructor.name, this] );

    //% slot
    this.destruct = function()
    {
        if( parent )
        {
            parent.removeChild( this );
        }
    };
}

